
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

/**
 * Clase encargada de cifrado de archivos y cadenas, así como de recuperar el hash de un fichero
 * @author epindado
 */
public class CifrarDatos {

    /**
     * 
     * @param datos a cifrar
     * @param clave es un String de largo múltiplo de 16 
     *      en este caso si es de largo 32 nos permite AES-256 (32 * 8) 
     * @return 
     */
    public byte[] cifrado (String datos, String clave){

        SecretKeySpec key = new SecretKeySpec(clave.getBytes(), "AES");
        Cipher cipher;
        byte[] datosCifrados = new byte[100];
        try {
            cipher = Cipher.getInstance("AES");
            //Comienzo a encriptar
            cipher.init(Cipher.ENCRYPT_MODE, key);

            datosCifrados = cipher.doFinal(datos.getBytes());

        } catch (IllegalBlockSizeException ex) {
            Logger.getLogger(CifrarDatos.class.getName()).log(Level.SEVERE, null, ex);
        } catch (BadPaddingException ex) {
            Logger.getLogger(CifrarDatos.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InvalidKeyException ex) {
            Logger.getLogger(CifrarDatos.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(CifrarDatos.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchPaddingException ex) {
            Logger.getLogger(CifrarDatos.class.getName()).log(Level.SEVERE, null, ex);
        } catch (Exception e) {
            Logger.getLogger(FirmaPDF.class.getName()).log(Level.SEVERE, null, e);
        } finally {
            return datosCifrados;
        }
    }

    /**
     * Método encargado de cifrar mediante AES el archivo del que se envía la ruta
     * @param ruta
     * @param clave es un String de largo múltiplo de 16 
     *      en este caso si es de largo 32 nos permite AES-256 (32 * 8) 
     * @return 
     */
    public byte[] cifradoFichero (String ruta, String clave){

        SecretKeySpec key = new SecretKeySpec(clave.getBytes(), "AES");
        Cipher cipher;
        byte[] datosCifrados = new byte[100];
        try {
            cipher = Cipher.getInstance("AES");
            //Comienzo a encriptar
            cipher.init(Cipher.ENCRYPT_MODE, key);

            FileInputStream fis = new FileInputStream(ruta);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] buf = new byte[1024];

            for (int readNum; (readNum = fis.read(buf)) != -1;) {
                bos.write(buf, 0, readNum);
            }

            byte[] bytes = bos.toByteArray();

            datosCifrados = cipher.doFinal(bytes);

        } catch (IllegalBlockSizeException ex) {
            Logger.getLogger(CifrarDatos.class.getName()).log(Level.SEVERE, null, ex);
        } catch (BadPaddingException ex) {
            Logger.getLogger(CifrarDatos.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InvalidKeyException ex) {
            Logger.getLogger(CifrarDatos.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(CifrarDatos.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchPaddingException ex) {
            Logger.getLogger(CifrarDatos.class.getName()).log(Level.SEVERE, null, ex);
        } catch (Exception e) {
            Logger.getLogger(FirmaPDF.class.getName()).log(Level.SEVERE, null, e);
        } finally {
            return datosCifrados;
        }
    }

    /**
     * Método encargado de descrifrar un algoritmo AES
     * @param datos
     * @param clave es un String de largo múltiplo de 16 
     *      en este caso si es de largo 32 nos permite AES-256 (32 * 8) 
     * @return 
     */
    public String desCifrado (byte[] datos, String clave){

        SecretKeySpec key = new SecretKeySpec(clave.getBytes(), "AES");
        Cipher cipher;
        String datosDescifrados = "";
        try {
            cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] datosDecifrados = cipher.doFinal(datos);

            datosDescifrados = new String(datosDecifrados); 

        } catch (IllegalBlockSizeException ex) {
            Logger.getLogger(CifrarDatos.class.getName()).log(Level.SEVERE, null, ex);
        } catch (BadPaddingException ex) {
            Logger.getLogger(CifrarDatos.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InvalidKeyException ex) {
            Logger.getLogger(CifrarDatos.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(CifrarDatos.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchPaddingException ex) {
            Logger.getLogger(CifrarDatos.class.getName()).log(Level.SEVERE, null, ex);
        } catch (Exception e) {
            Logger.getLogger(FirmaPDF.class.getName()).log(Level.SEVERE, null, e);
        } finally {
            return datosDescifrados;
        }
    }

    /**
     * Recupera el hash único de el fichero del que se envía la ruta
     * @param ruta
     * @return 
     */
    public String recuperarHash (String ruta){
        String digestB64 = "";
        try {
            FileInputStream fis = new FileInputStream(ruta);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] buf = new byte[1024];

            for (int readNum; (readNum = fis.read(buf)) != -1;) {
                bos.write(buf, 0, readNum);
            }

            byte[] bytes = bos.toByteArray();

            MessageDigest md = MessageDigest.getInstance("SHA-256");
            byte[] thedigest = md.digest(bytes);    
            // Codificamos en Base64 para que sea más legible, no en binario
            digestB64 = DatatypeConverter.printBase64Binary(thedigest);
        } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(CifrarDatos.class.getName()).log(Level.SEVERE, null, ex);
        } catch (Exception ex) {
            Logger.getLogger(CifrarDatos.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            return digestB64;
        }
    }
}